<!--
 * @Author: liyin 2179253556@qq.com
 * @Date: 2023-09-11 16:45:05
 * @LastEditors: Please set LastEditors
 * @LastEditTime: 2025-04-14 16:09:52
 * @FilePath: DormitAttend.vue
 * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
-->
<template>
    <div class="content-box">
        <!-- 加载层 -->
        <div class="loading-flex-box" v-show="loading">
            <dv-loading>加载中...</dv-loading>
        </div>
        <!-- 头部 -->
        <Header ref="header"></Header>
        <!-- 中间内容部分 -->
        <div class="screenBoxTwo" ref="screenBoxHeight" :style="'height:' + screenBoxHeight + 'px;'">
            <div class="dormitModule" ref="dormitModuleRef">
                <div class="name" ref="nameRef">数据统计时间：{{ headerShowTime.substring(0, 11) }}
                    <span v-for="(item, index) in ruleName" :key="index">
                        {{ item }}
                        <em v-if="index != ruleName.length - 1">、</em>
                    </span>
                </div>
                <div class="contentBox" :style="'height:' + contentHeight + 'px;'">
                    <div class="lfBox">
                        <div class="listFlex" ref="listFlexRef">
                            <div class="listItem">
                                <div>{{ Number(inOutStatus[0]) !== 1 ? '已归宿' : '已离宿' }}学生</div>
                                <div>{{ countInfo.total_normal }}</div>
                            </div>
                            <div class="listItem">
                                <div>{{ Number(inOutStatus[0]) !== 1 ? '未归宿' : '未离宿' }}学生</div>
                                <div style="color: #FA5C60;">{{ countInfo.total_absence }}</div>
                            </div>
                            <div class="listItem">
                                <!--  0：进 1：出  -->
                                <!-- status 正常：1, 晚归/晚出 2 -->
                                <div>{{ Number(inOutStatus[0]) !== 1 ? '晚归' : '晚出' }}学生</div>
                                <div style="color: #FF9C0D;">{{ Number(inOutStatus[0]) !== 1 ? countInfo.total_late_gui
                                    :
                                    countInfo.total_late_chu
                                    }}</div>
                            </div>
                        </div>
                        <div class="slideFlex">
                            <div class="swiper-container">
                                <div class="swiper-wrapper" ref="mySwiper">
                                    <div class="swiper-slide" v-for="(items, indexs) in swiperListNew" :key="indexs">
                                        <el-row :gutter="$util.nowSize(26)">
                                            <el-col v-for="(item, index) in items" :key="index" :span="12">
                                                <div class="slideItem">
                                                    <!-- 进  -->
                                                    <div class="lfItem" v-if="Number(inOutStatus[0]) !== 1"
                                                        :style="Number(item.att_status) == 0 ? 'background: #FA5C60;' : Number(item.att_status) == 2 ? 'background: #FF9C0D;' : 'background:#04D7E3'">
                                                        {{ Number(item.att_status) == 0 ? '未归' : Number(item.att_status)
                                                        == 2 ? '晚归' : '已归' }}
                                                    </div>
                                                    <!-- 出 -->
                                                    <div class="lfItem" v-else
                                                        :style="Number(item.att_status) == 0 ? 'background: #FA5C60;' : Number(item.att_status) == 2 ? 'background: #FF9C0D' : 'background:#04D7E3'">
                                                        {{ Number(item.att_status) == 0 ? '未离' :
                                                        Number(item.att_status) == 2 ? '晚出' : '已离' }}
                                                    </div>
                                                    <div class="rtItem">
                                                        <div>{{ item.name }}-{{ item.class_name }}</div>
                                                        <div>{{ item.room }}</div>
                                                    </div>
                                                </div>
                                            </el-col>
                                        </el-row>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>
                    <div class="rtBox">
                        <div class="hearderTitle" ref="hearderTitleRef">
                            各楼层 / 寝室在寝人数
                        </div>
                        <DormitScroll :dataNum="list" :dataNumHeight="dataNumHeight" :inOutStatus="inOutStatus[0]"
                            :dataNumBol="dataNumBol" @setdataNumBol="setdataNumBol"></DormitScroll>
                    </div>
                </div>
            </div>
        </div>
        <!-- socket通讯断了 -->
        <div class="loading-flex-box" v-show="socketStatus">
            <div class="socket-err-box column">
                <div class="q">
                    <div class="a a1"></div>
                    <div class="a a2"></div>
                    <div class="a a3"></div>
                    <div class="a a4"></div>
                    <div class="a a5"></div>
                    <div class="btn"></div>
                </div>
                通讯链接异常，请耐心等待!
                <div class="update-err-box">
                    正在为您重新连接<span class="dotting"></span>
                </div>
            </div>
        </div>
        <!-- 底部 -->
        <Footer ref="footer"></Footer>
    </div>
</template>
<script>
import { mapState, mapActions, mapMutations } from 'vuex'
import Header from './Common/Header.vue'
import Footer from './Common/Footer.vue'
import DormitScroll from './DormitScroll.vue'
import socket from "../websocket/socket";
import $util from '../util/util'
import Swiper from 'swiper';
import 'swiper/css/swiper.min.css';
export default {
    name: 'DormitAttend',
    components: {
        Header,
        Footer,
        DormitScroll
    },
    data() {
        return {
            mySwiper: null,
            screenBoxHeight: 0,
            loading: false,
            dataNumBol: false,
            contentHeight: 0,
            list: [],
            swiperList: [],
            swiperListNew: [],
            activeIndex: 0,
            showNum: 6, // 左边可以展示的数量
            dataNumHeight: 0,
            deviceIds: '',
            animate: false,
            countInfo: {
                total_normal: '',
                total_absence: '',
                total_late_gui: '',
                total_late_chu: '',
            },
            inOutStatus: [],
            // 存储推送过来的学生id,避免重复计算
            pushStuIds: [],
            // 右侧轮播数据没转格式的时候
            dataList: [],
            loading: false, // 加载层
            headerShowTime: '',
            ruleTimeRange: '',
            ruleIds: [],
            ruleName: []
        }
    },
    computed: {
        ...mapState(['socketStatus'])
    },
    beforeDestroy() {
        // 取消监听事件
        window.removeEventListener('resize', this.onresize)
        window.removeEventListener("setItemEvent")
        window.removeEventListener("onmessageWS", this.getSocketData)
    },
    async mounted() {
        this.loading = true
        this.headerShowTime = window.localStorage.getItem('headerShowTime')
        this.ruleTimeRange = window.localStorage.getItem('ruleTimeRange')
        this.ruleIds = JSON.parse(window.localStorage.getItem('ruleIds'))
        await this.getHeight()
        // 头部的数居统计
        await this.getTodayRecord()
        // 左下角的轮播和右边的轮播
        await this.getLine()
        // setTimeout(async () => {
        //     await this.getLine()
        // }, 7 * 1000);
        this.loading = false
        // 获取设备信息
        this.getInfo()
        // 初始化websocket
        socket.initWebSocket()
        // // 添加socket通知监听
        window.addEventListener("onmessageWS", this.getSocketData)
        // 浏览器窗口变化的时候
        window.addEventListener('resize', () => {
            setTimeout(async () => {
                await this.getHeight()
            }, 100)
        })
        //监听localStorage变化
        window.addEventListener("setItemEvent", async (e) => {
            // 监听时间的变化
            if (e.key === 'ruleIds') {
                if (JSON.parse(e.newValue).length > 0 && e.newValue !== JSON.stringify(this.ruleIds)) {
                    // console.log(e.newValue, 'jianlele', JSON.stringify(this.ruleIds))
                    this.ruleIds = JSON.parse(e.newValue)
                    //考勤规则变化了 重新刷新数据
                    this.loading = true
                    // 头部的数居统计
                    await this.getTodayRecord()
                    // 左下角的轮播和右边的轮播
                    await this.getLine()
                    this.loading = false
                }
            } else if (e.key === 'ruleTimeRange') {
                // this.ruleTimeRange = e.newValue
            }
        })
    },
    methods: {
        // 左上角的数据
        async getSwiperInnit() {
            this.$nextTick(() => {
                this.mySwiper = new Swiper('.swiper-container', {
                    // Swiper配置参数，例如：自动播放，分页器等
                    loop: true, // 循环模式选项
                    slidesPerView: 1,
                    spaceBetween: $util.nowSize(40),
                    autoplay: {
                        delay: 1000 * 3, // 轮播间隔时间设置为3000毫秒（即3秒）
                        disableOnInteraction: false, // 这将允许在用户交互后继续自动播放
                    },
                    observer: true,  //修改swiper自己或子元素时，自动初始化swiper
                    observeParents: true,  //修改swiper的父元素时，自动初始化swiper
                    on: {
                        // slideChange: () => { this.updateData() }, // 监听slideChange事件来更新数据
                    },
                })
            })
        },
        // 网页加载即获取当前学校信息与时间等
        async getInfo() {
            const params = {
                scene_type: "screen",
            };
            const { data: res } = await this.$http.get('/WebSocket/Screen/', {
                params: params
            })
            if (res.code !== 1) {
                return this.$message.error(res.msg)
            }
            this.deviceIds = res.data.device_ids
        },
        async getSocketData(res) {
            let info = res.detail.data
            console.log(res.detail.data, '发生那个数据')
            let floorName = '' // 记录宿舍名称
            // 判断是否是本机的ip，是才需要更换
            let deviceVal = this.deviceIds ? this.deviceIds.split(',') : []
            if (Number(info.scene) === 10089) {
                if (deviceVal.includes(info.device_id)) {
                    // 下面的逻辑
                    // 判断是否有推送过来过
                    if (!this.pushStuIds.includes(info.user_id)) {
                        this.pushStuIds.push(info.user_id)
                        // 为推送过来 左边旧统计数据和状态改变
                        this.swiperListNew.forEach(item => {
                            item.forEach(items => {
                                if (Number(items.student_id) == Number(info.user_id)) {
                                    floorName = items.floor
                                    if (Number(info.att_status) === 1) {
                                        // 如果推过来是正常状态
                                        // 签到人数+1
                                        this.countInfo.total_normal = Number(this.countInfo.total_normal) + 1
                                        // 缺勤人数-1
                                        this.countInfo.total_absence = Number(this.countInfo.total_absence) > 0 ? Number(this.countInfo.total_absence) - 1 : 0
                                    }
                                    if (Number(info.att_status) === 2) {
                                        // 如果推过来是晚归或者晚出
                                        if (Number(this.inOutStatus[0]) != 1) {
                                            this.countInfo.total_late_gui = Number(this.countInfo.total_late_gui) + 1
                                        } else {
                                            this.countInfo.total_late_chu = Number(this.countInfo.total_late_chu) + 1
                                        }
                                    }
                                    items.att_status = info.att_status
                                }
                            })
                        })
                        // 计算右侧是的数据 实时变化
                        this.dataList.forEach(item => {
                            if (floorName === item.name) {
                                // 如果房间号相同 就数值相对加减
                                if (Number(info.att_status) === 1) {
                                    // 如果推过来是正常状态
                                    // 签到人数+1
                                    item.normal = Number(item.normal) + 1
                                    // 缺勤人数-1
                                    item.absence = Number(item.absence) > 0 ? Number(item.absence) - 1 : 0
                                }
                                if (Number(info.att_status) === 2) {
                                    // 如果推过来是晚归或者晚出
                                    item.late = Number(item.late) + 1
                                    // 缺勤人数-1
                                    item.absence = Number(item.absence) > 0 ? Number(item.absence) - 1 : 0
                                }
                            }
                        })
                        this.fomateList(this.dataList)
                    }
                }
            }
        },
        // 将数组分为几组
        // 分组方法三个参数分别是 新数组，原数组，几个分为一组
        groupArray(newArr, arr, num) {
            if (arr.length > 0) {
                if (arr.length > num) { //arr 的长度大于 分组数
                    newArr.push(arr.splice(0, num)) //分组
                    this.groupArray(newArr, arr, num) //递归
                } else {
                    //如果长度不够分为一组，就直接把剩余的分为一组
                    newArr.push(arr.splice(0, arr.length))
                }
            }
            return newArr //返回已分组的新数组
        },
        //  获取上面的总数统计
        async getTodayRecord() {
            let ruleId = []
            let addTime = []
            this.inOutStatus = []
            this.ruleName = []
            if (this.ruleIds.length > 0) {
                this.ruleIds.forEach(item => {
                    ruleId.push(item.split('/')[0])
                    addTime.push(item.split('/')[1])
                    if (!this.inOutStatus.includes(item.split('/')[2])) {
                        this.inOutStatus.push(item.split('/')[2])
                    }
                    this.ruleName.push(item.split('/')[3])
                })
            }
            const params = {
                type: 'attendanceStatisticsNew',
                floor_id: window.localStorage.getItem('floorId'),
                id: ruleId.length > 0 ? ruleId.join(',') : '',
                add_time: addTime.length > 0 ? addTime.join(',') : '',
                faculty_name: Number(JSON.parse(window.localStorage.getItem('routerParams')).type) == 1 ? '初中' : '高中'
            }
            const { data: res } = await this.$http.get('/Dormitory/DataScreen/', {
                params: params
            })
            if (res.code !== 1) return;
            this.countInfo = res.total
        },
        // 获取右侧 
        async getLine() {
            let ruleId = []
            let addTime = []
            this.inOutStatus = []
            if (this.ruleIds.length > 0) {
                this.ruleIds.forEach(item => {
                    ruleId.push(item.split('/')[0])
                    addTime.push(item.split('/')[1])
                    if (!this.inOutStatus.includes(item.split('/')[2])) {
                        this.inOutStatus.push(item.split('/')[2])
                    }
                })
            }
            const params = {
                type: 'getFloorRoomAttendanceStatistics',
                floor_id: window.localStorage.getItem('floorId'),
                id: ruleId.length > 0 ? ruleId.join(',') : '',
                add_time: addTime.length > 0 ? addTime.join(',') : '',
                faculty_name: Number(JSON.parse(window.localStorage.getItem('routerParams')).type) == 1 ? '初中' : '高中'
            }
            const { data: res } = await this.$http.get('/Dormitory/DataScreen/', {
                params: params
            })
            if (res.code !== 1) {
                this.loading = false
                if (res.code !== 1) return;
            }
            if (this.mySwiper) {
                this.mySwiper.destroy(true)
                this.mySwiper = null
            }
            let arr = []
            arr = [...res.data.att_list.absence_list, ...res.data.att_list.late_list]
            if (arr.length > 0) {
                this.swiperListNew = this.groupArray([], JSON.parse(JSON.stringify(arr)), this.showNum)
                await this.getSwiperInnit()
            }
            this.dataList = res.data.floor_statistic
            this.fomateList(this.dataList)

        },
        // 数据格式转化
        async fomateList(list) {
            let objList = []
            if (list.length > 0) {
                list.forEach((item, index) => {
                    let arr = []
                    arr.push(`<span style="color:#ffffff;font-size:${$util.nowSize(22)}px;">${item.name}</span>`)
                    arr.push(`<span style="color:#ffffff;font-size:${$util.nowSize(22)}px;">${item.normal}</span>`)
                    arr.push(`<span style="color:#ffffff;font-size:${$util.nowSize(22)}px;">${item.absence}</span>`)
                    arr.push(`<span style="color:#ffffff;font-size:${$util.nowSize(22)}px;">${item.late}</span>`)
                    objList.push(arr)
                })
                this.list = [...objList]
            }
        },
        async getHeight() {
            this.screenBoxHeight = document.body.clientHeight - this.$refs.header.$el.clientHeight - this.$refs.footer.$el.clientHeight
            this.contentHeight = this.screenBoxHeight - this.$refs.nameRef.clientHeight
            console.log(this.contentHeight)
            this.dataNumHeight = this.contentHeight - this.$refs.hearderTitleRef.clientHeight - $util.nowSize(10)
            // 左边
            this.showNum = Math.floor((this.contentHeight - this.$refs.listFlexRef.clientHeight) / $util.nowSize(179)) * 2
        },
        setdataNumBol(bol) {
            this.dataNumBol = bol
        },
    }
}
</script>
<style lang="less" scoped>
.dormitModule {
    width: 100%;
    height: 100%;
    color: #FFFFFF;

    .name {
        font-weight: bold;
        font-size: 28px;
        color: #FFFFFF;
        text-align: center;
        width: 100%;
        padding: 40px;
        box-sizing: border-box;
    }

    .contentBox {
        display: flex;
        padding: 0 40px 0 40px;

        .lfBox {
            flex: 1;
            overflow: hidden;

            .listFlex {
                .listItem {
                    width: 360px;
                    height: 90px;
                    display: inline-block;
                    background-image: url('../assets/img/k.png');
                    background-size: 100% 100%;
                    display: inline-flex;
                    align-items: center;
                    justify-content: space-between;
                    padding: 0 20px;
                    padding-top: 10px;
                    box-sizing: border-box;
                    margin-right: 24px;

                    div:first-of-type {
                        font-weight: bold;
                        font-size: 22px;
                        color: #05F4F9;
                    }

                    div:last-of-type {
                        font-weight: bold;
                        font-size: 40px;
                        color: #05F4F9;
                    }
                }

                .listItem:last-of-type {
                    margin-right: 0px;
                }
            }

            .slideFlex {
                position: relative;

                .swiper-slide {
                    width: 100%;

                    .slideItem {
                        width: 544px;
                        display: inline-flex;
                        align-items: center;
                        background: #04216E;
                        border-radius: 4px;
                        border: 1px solid #004DBB;
                        margin-top: 40px;
                        padding: 20px 30px;
                        height: 140px;
                        box-sizing: border-box;

                        .lfItem {
                            width: 96px;
                            height: 96px;
                            background: #FA5C60;
                            border-radius: 4px;
                            margin-right: 30px;
                            font-size: 26px;
                            display: flex;
                            align-items: center;
                            justify-content: center;
                        }

                        .rtItem {
                            font-weight: bold;
                            font-size: 26px;

                            div:first-of-type {
                                margin-bottom: 15px;
                            }

                        }
                    }

                    .slideItem:nth-of-type(2n+1) {
                        margin-right: 40px;
                    }
                }
            }
        }

        .rtBox {
            margin-left: 40px;
            width: 672px;
            height: 100%;
            background: #00124B;
            box-shadow: inset 0px 0px 40px 1px #0053B0;
            border-radius: 4px 4px 4px 4px;
            border: 2px solid #004DBB;
            box-sizing: border-box;

            .hearderTitle {
                width: 100%;
                height: 88px;
                background-image: url('../assets/img/bt.png');
                background-size: cover;
                font-weight: bold;
                font-size: 24px;
                color: #00FFFC;
                padding-top: 15px;
                box-sizing: border-box;
                text-align: center;
            }
        }
    }
}

.loading-flex-box {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background-color: rgba(0, 0, 0, 0.5);
    z-index: 9999;
    display: flex;
    justify-content: center;
    align-items: center;

    .socket-err-box {
        width: 360px;
        font-size: 20px;
        color: #0a1971;
        font-weight: bold;
        background-color: #fff;
        border-radius: 6px;
        text-align: center;
        padding: 30px 50px;
        display: flex;
        justify-content: center;
        align-items: center;
    }

    .update-err-box {
        color: #ffc900;
        margin-top: 10px;
    }

    .q {
        width: 100px;
        height: 100px;
        position: relative;
        margin-bottom: 20px;
    }

    .a {
        width: 60px;
        height: 60px;
        border: 1px solid #ffc900;
        background: #ffc900;
        position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
        border-radius: 50%;
        animation: identifier 3s infinite;
        // opacity: 0;
    }

    .a2 {
        animation-delay: 0.3s;
    }

    .a3 {
        animation-delay: 0.5s;
    }

    .a4 {
        animation-delay: 0.7s;
    }

    .a5 {
        animation-delay: 0.8s;
    }

    .btn {
        position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
        background: #ffc900;
        width: 40px;
        height: 40px;
        border-radius: 50%;
        text-align: center;
        line-height: 50px;
        opacity: 1;
    }

    .dotting {
        display: inline-block;
        width: 14px;
        min-height: 3px;
        padding-right: 3px;
        border-left: 3px solid currentColor;
        border-right: 3px solid currentColor;
        background-color: currentColor;
        background-clip: content-box;
        box-sizing: border-box;
        animation: dot 3s infinite step-start both;
    }

    .dotting:before {
        content: '...';
    }

    /* IE8 */
    .dotting::before {
        content: '';
    }

    :root .dotting {
        margin-left: 2px;
        padding-left: 2px;
    }
}

@keyframes identifier {
    0% {
        transform: translate(-50%, -50%) scale(0);
        opacity: 0.8;
    }

    100% {
        transform: translate(-50%, -50%) scale(1.8);
        opacity: 0;
    }
}

@keyframes dot {
    25% {
        border-color: transparent;
        background-color: transparent;
    }

    /* 0个点 */
    50% {
        border-right-color: transparent;
        background-color: transparent;
    }

    /* 1个点 */
    75% {
        border-right-color: transparent;
    }

    /* 2个点 */
}

.anim {
    transition: all 0.5s;
}
</style>
